import pandas as pd
import os
from fastdtw import fastdtw
from scipy.spatial.distance import euclidean
from concurrent.futures import ProcessPoolExecutor #这种方法可以利用多核处理器的优势，同时计算多对轨迹之间的距离。
from shapely.geometry import LineString

# 这个.py文件是第一版，可以不看，看Separate_ByMMSI_Improvement.py即可，这两个函数的初级处理模块几乎一直

def process_csj(file_path):# CSJ处理逻辑
    columns_csj = ['MMSI', 'Timestamp', 'TimeDelta', 'LATITUDE', 'LONGITUDE', 'SOG', 'Ignore']
    df = pd.read_csv(file_path, sep=' ', names=columns_csj, usecols=['MMSI', 'Timestamp', 'TimeDelta', 'LATITUDE', 'LONGITUDE', 'SOG'])
    df['Timestamp'] = pd.to_datetime(df['Timestamp'], format='%Y%m%d%H%M%S')
    df = df[['MMSI', 'Timestamp', 'LONGITUDE', 'LATITUDE', 'SOG']]  # 按照指定顺序选择列并排除TimeDelta
    return df

def process_zs(file_path, mmsi, subdir):# ZS处理逻辑
    df = pd.read_csv(file_path, usecols=['Date_Time', 'SOG', 'LONGITUDE', 'LATITUDE'])
    if subdir == "2018-4-24":
        df['Date_Time'] += 86400  # 加上一天的秒数
    df.rename(columns={'Date_Time': 'Timestamp'}, inplace=True)
    df['Timestamp'] = pd.to_datetime(df['Timestamp'], unit='s', origin=pd.Timestamp('2018-04-23'))
    df['MMSI'] = mmsi
    df = df[['MMSI', 'Timestamp', 'LONGITUDE', 'LATITUDE', 'SOG']]  # 按照指定顺序选择列并排除TimeDelta
    return df

def process_cfd(file_path, mmsi, subdir):
    # 提取日期中的天数，并转换为对应的秒数（距离2018-06-01的天数乘以每天的秒数）
    day = int(subdir.split('-')[-1])  # 提取日期的天
    seconds_from_start = (day - 1) * 86400  # 从2018-06-01开始的秒数
    # 读取文件内容，手动指定列名（注意这里不再包含MMSI）
    columns_cfd = ['Timestamp', 'TimeDelta', 'LONGITUDE', 'LATITUDE', 'Ignore', 'COG', 'SOG', ]
    df = pd.read_csv(file_path, names=columns_cfd, usecols=['TimeDelta', 'LONGITUDE', 'LATITUDE', 'SOG'])
    # 调整时间戳
    df['TimeDelta'] += seconds_from_start
    # 将时间戳转换为实际的DateTime
    df['Timestamp'] = pd.to_datetime(df['TimeDelta'], unit='s', origin=pd.Timestamp('2018-06-01'))
    # 由于文件名即MMSI，这里我们添加一个MMSI列，所有行都使用该MMSI值
    df['MMSI'] = mmsi
    df = df[['MMSI', 'Timestamp', 'LONGITUDE', 'LATITUDE', 'SOG']]  # 按照指定顺序选择列并排除TimeDelta
    return df

# 通过计算不同MMSI之间轨迹的DTW相似度来进行分析。这里，我们将每个MMSI的轨迹视为一个完整的时间序列，
# 并使用经度、纬度和SOG（速度）作为特征来进行比较。
# 我们会先选取部分数据（例如前100个MMSI）进行分析，以便更快地得到结果并验证方法的可行性。
def calculate_dtw_distance(pair):
    df1, df2 = pair
    # 提取经纬度和速度作为特征
    trajectory1 = df1[['LONGITUDE', 'LATITUDE', 'SOG']].to_numpy()
    trajectory2 = df2[['LONGITUDE', 'LATITUDE', 'SOG']].to_numpy()
    # 使用fastdtw计算两个轨迹之间的距离
    distance, path = fastdtw(trajectory1, trajectory2, dist=euclidean)
    return distance

def compute_dtw_distances_concurrently(selected_mmsis, all_trajectories):
    # 初始化一个空字典来存储DTW距离
    dtw_distances = {}
    # 使用ProcessPoolExecutor来并行化计算
    with ProcessPoolExecutor(max_workers=8) as executor:
        # 创建一个映射，将future映射到MMSI对
        future_to_mmsi_pair = {}
        for i, mmsi1 in enumerate(selected_mmsis):
            for j, mmsi2 in enumerate(selected_mmsis):
                if i < j:  # 避免重复计算
                    # 提交任务并记录对应的MMSI对
                    future = executor.submit(calculate_dtw_distance, (all_trajectories[mmsi1], all_trajectories[mmsi2]))
                    future_to_mmsi_pair[future] = (mmsi1, mmsi2)
        # 等待所有任务完成并收集结果
        for future in future_to_mmsi_pair:
            mmsi1, mmsi2 = future_to_mmsi_pair[future]
            distance = future.result()  # 获取计算结果
            dtw_distances[(mmsi1, mmsi2)] = distance

    return dtw_distances


def main():
    dirs = {
        './Data/CSJ': process_csj,
        # './Data/ZS': process_zs,
        # './Data/CFD': process_cfd
    }
    all_trajectories = {}

    for root_dir, process_func in dirs.items():
        if root_dir.endswith('CSJ'):  # CSJ逻辑
            for file_name in os.listdir(root_dir):
                if file_name.endswith('.txt'):
                    file_path = os.path.join(root_dir, file_name)
                    df = process_csj(file_path)
                    # 使用从DataFrame中提取的MMSI作为键来更新all_trajectories字典。这确保了使用文件中实际的MMSI值，而不是文件名来索引轨迹数据。
                    # 确保DataFrame不为空，然后从第一行获取MMSI作为键
                    if not df.empty:
                        # 假设MMSI是DataFrame中的第一列
                        mmsi = df.iloc[0]['MMSI']
                        all_trajectories.setdefault(mmsi, []).append(df)

        elif root_dir.endswith('CFD'):
            for subdir in os.listdir(root_dir):
                subdir_path = os.path.join(root_dir, subdir)
                if os.path.isdir(subdir_path):
                    for file_name in os.listdir(subdir_path):
                        if file_name.endswith('.csv'):
                            file_path = os.path.join(subdir_path, file_name)
                            mmsi = file_name.split('.')[0]
                            df = process_cfd(file_path, mmsi, subdir)
                            all_trajectories.setdefault(mmsi, []).append(df)

        else:  # ZS逻辑，包含子文件夹
            for subdir in os.listdir(root_dir):
                subdir_path = os.path.join(root_dir, subdir)
                if os.path.isdir(subdir_path):
                    for file_name in os.listdir(subdir_path):
                        if file_name.endswith('.csv'):
                            file_path = os.path.join(subdir_path, file_name)
                            mmsi = file_name.split('.')[0]
                            df = process_func(file_path, mmsi, subdir)
                            all_trajectories.setdefault(mmsi, []).append(df)

    # 合并同一MMSI的DataFrame
    for mmsi, dfs in all_trajectories.items():
        all_trajectories[mmsi] = pd.concat(dfs).reset_index(drop=True).sort_values(by='Timestamp')

    # 示例输出
    # for mmsi, df in list(all_trajectories.items())[:1]:
    #     print(f"MMSI: {mmsi}, Data: \n{df.head(200)}\n")

    sorted_mmsis = sorted(all_trajectories.keys(), key=lambda x: int(x))
    selected_mmsis = sorted_mmsis  # 选取前10个MMSI进行分析
    dtw_distances = compute_dtw_distances_concurrently(selected_mmsis, all_trajectories)

    # # 根据MMSI排序，并更新all_trajectories字典
    # sorted_mmsis = sorted(all_trajectories.keys(), key=lambda x: int(x))
    #
    # # 选取前10个MMSI进行DTW分析
    # selected_mmsis = sorted_mmsis
    #
    # print(selected_mmsis)
    #
    # # 初始化一个空字典来存储DTW距离
    # dtw_distances = {}
    #
    # # 计算选定MMSI之间的DTW距离
    # for i, mmsi1 in enumerate(selected_mmsis):
    #     for j, mmsi2 in enumerate(selected_mmsis):
    #         if i < j:  # 避免重复计算
    #             distance = calculate_dtw_distance(all_trajectories[mmsi1], all_trajectories[mmsi2])
    #             dtw_distances[(mmsi1, mmsi2)] = distance

    # # 输出一些DTW距离的示例
    # for key, distance in list(dtw_distances.items()):
    #     print(f"DTW distance between {key[0]} and {key[1]}: {distance}")

    # 初始化一个空列表来存储DTW距离记录
    dtw_distances_records = []

    # 计算选定MMSI之间的DTW距离，并保存记录到列表
    for key, distance in dtw_distances.items():
        dtw_distances_records.append({
            'MMSI1': key[0],
            'MMSI2': key[1],
            'DTW_Distance': distance
        })

    # 将记录转换为DataFrame
    dtw_distances_df = pd.DataFrame(dtw_distances_records)

    # 输出所有DTW距离的记录
    # print(dtw_distances_df)

    # 保存到CSV文件
    dtw_distances_df.to_csv('./Data/DTW/CFD/dtw_distances.csv', index=False)

#     看到DTW距离的数值很大是正常的，因为DTW计算的是两个序列之间的累计差异。如果序列长且特征值（如经纬度和速度）的差异较大，累计的距离自然会很大。
#     这并不意味着方法有误，而是这些数值反映了轨迹之间的相对差异大小。

if __name__ == "__main__":
    main()

